package com.yuven.baselib.utils;

import android.annotation.SuppressLint;
import android.os.Environment;
import android.os.Process;
import android.os.StatFs;
import android.util.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public final class Logger {
    private static final String LOG_TAG = "Logger";

    public static final int VERBOSE = 2;
    public static final int DEBUG = 3;
    public static final int INFO = 4;
    public static final int WARN = 5;
    public static final int ERROR = 6;
    public static final int NONE = 99;
    private volatile static int currentLevel = 2;
    private volatile static boolean isStoreLogFile = false;
    private static final int FILE_AMOUNT = 5;
    private static final long MAXSIZE_PERFILE = 5 * 1048576L;
    private static List<LogAdapter> logAdapterList = new ArrayList<>();

    private Logger() {
    }

    public static void addLogAdapter(LogAdapter logAdapter){
        if(logAdapter!=null){
            logAdapterList.add(logAdapter);
        }
    }

    public static void initLogger(int logLevel, boolean isStore, String storePath) {
        currentLevel = logLevel;
        isStoreLogFile = isStore;

        if (isStore) {
            if (storePath != null) {
                LogCache.getInstance().initLogWriter(storePath, 5, MAXSIZE_PERFILE);
            } else {
                isStoreLogFile = false;
            }
        }

        if (!isStoreLogFile && LogCache.getInstance().isStarted()) {
            LogCache.getInstance().stop();
        }

    }

    @SuppressLint("LogTagMismatch")
    public static int println(int level, String tag, String msg) {
        int result = 0;
        if(logAdapterList.size()>0){
            //TODO 解耦打印、存储日志逻辑
            for (LogAdapter adapter:logAdapterList){
                adapter.log(level,tag,msg);
            }
        }else {
            //TODO 弃用
            if (isLoggable(level)) {
                result = Log.println(level, tag, msg);
            }
            if (isStoreLogFile || LogCache.getInstance().isLogWriterInit()) {
                if (!LogCache.getInstance().isStarted()) {
                    startService();
                }

                LogCache.getInstance().write(levelString(level), tag, msg);
            }
        }

        return result;
    }

    private static String levelString(int level) {
        switch (level) {
            case 2:
                return "V";
            case 3:
                return "D";
            case 4:
                return "I";
            case 5:
                return "W";
            case 6:
                return "E";
            default:
                return "D";
        }
    }

    public static int v(String msg) {
        return println(2, LOG_TAG, msg);
    }

    public static int v(String tag, String msg) {
        return println(2, tag, msg);
    }

    public static int v(String tag, String msg, Throwable tr) {
        return println(2, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int d(String msg) {
        return println(3, LOG_TAG, msg);
    }

    public static int d(String tag, String msg) {
        return println(3, tag, msg);
    }

    public static int d(String tag, String msg, Throwable tr) {
        return println(3, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int i(String msg) {
        return println(4, LOG_TAG, msg);
    }

    public static int i(String tag, String msg) {
        return println(4, tag, msg);
    }

    public static int i(String tag, String msg, Throwable tr) {
        return println(4, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int w(String msg) {
        return println(5, LOG_TAG, msg);
    }

    public static int w(String tag, String msg) {
        return println(5, tag, msg);
    }

    public static int w(String tag, String msg, Throwable tr) {
        return println(5, tag, msg + '\n' + getStackTraceString(tr));
    }

    public static int w(String tag, Throwable tr) {
        return println(5, tag, getStackTraceString(tr));
    }

    public static int e(String msg) {
        return println(6, LOG_TAG, msg);
    }

    public static int e(String tag, String msg) {
        return println(6, tag, msg);
    }

    public static int e(String tag, Throwable tr) {
        return println(6, tag, getStackTraceString(tr));
    }

    public static int e(String tag, String msg, Throwable tr) {
        return println(6, tag, msg + '\n' + getStackTraceString(tr));
    }

    private static String getStackTraceString(Throwable tr) {
        return Log.getStackTraceString(tr);
    }

    private static boolean isLoggable(int level) {
        return level >= currentLevel;
    }

    private static synchronized void startService() {
        LogCache.getInstance().start();
    }

    private static class LogCache {
        public static final String TAG = "Logger";
        private static LogCache INSTANCE = null;
        private static final GregorianCalendar CALENDAR_INSTANCE = new GregorianCalendar();
        private final BlockingQueue<String> queue = new LinkedBlockingQueue();
        private volatile boolean started;
        private volatile Thread logWorkerThread;
        private volatile LogWriter logWriter = null;
        private int counter = 0;

        private LogCache() {
        }

        public static LogCache getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new LogCache();
            }

            return INSTANCE;
        }

        public void initLogWriter(String filePath, int fileAmount, long maxSize) {
            this.logWriter = LogWriter.getInstance(new File(filePath), fileAmount, maxSize);
        }

        public void write(String msg) {
            if (this.started) {
                try {
                    this.queue.put(msg);
                } catch (InterruptedException var3) {
                    Log.w("LogCache", msg, var3);
                }
            }

        }

        @SuppressLint("WrongConstant")
        public void write(String level, String tag, String msg) {
            CALENDAR_INSTANCE.setTimeInMillis(System.currentTimeMillis());
            int pid = Process.myPid();
            int month = CALENDAR_INSTANCE.get(2) + 1;
            int date = CALENDAR_INSTANCE.get(5);
            int hour = CALENDAR_INSTANCE.get(11);
            int minute = CALENDAR_INSTANCE.get(12);
            int seconds = CALENDAR_INSTANCE.get(13);
            StringBuilder sbr = new StringBuilder();
            sbr.append(month).append('-').append(date).append(' ');
            sbr.append(hour).append(':').append(minute).append(':').append(seconds);
            sbr.append('\t').append(level).append('\t').append(pid);
            sbr.append('\t').append('[').append(Thread.currentThread().getName()).append(']');
            sbr.append('\t').append(tag).append('\t').append(msg);
            this.write(sbr.toString());
        }

        public boolean isLogWriterInit(){
            return this.logWriter != null;
        }

        public boolean isStarted() {
            return this.started;
        }

        public synchronized void start() {
            if (this.logWorkerThread == null) {
                this.logWorkerThread = new Thread(new LogTask(), "Log Worker Thread - " + this.counter);
            }

            if (!this.started && logWriter !=null  && this.logWriter.initialize()) {
                this.started = true;
                this.logWorkerThread.start();
            }
        }

        public synchronized void stop() {
            if (this.started) {
                this.started = false;
                this.queue.clear();
                this.logWriter.close();
                if (this.logWorkerThread != null) {
                    this.logWorkerThread.interrupt();
                    this.logWorkerThread = null;
                }

            }
        }

        private final class LogTask implements Runnable {
            public LogTask() {
                LogCache.this.counter = LogCache.this.counter + 1;
            }

            private void dealMsg() throws InterruptedException {
                String msg = null;

                while (LogCache.this.started && !Thread.currentThread().isInterrupted()) {
                    msg = (String) LogCache.this.queue.take();

                    try {
                        synchronized (LogCache.this.logWriter) {
                            if (MemoryStatus.isExternalMemoryAvailable((long) msg.getBytes().length)) {
                                if (!LogCache.this.logWriter.isCurrentExist()) {
                                    if (!LogCache.this.logWriter.initialize()) {
                                        continue;
                                    }
                                } else if (!LogCache.this.logWriter.isCurrentAvailable() && !LogCache.this.logWriter.rotate()) {
                                    continue;
                                }

                                LogCache.this.logWriter.println(msg);
                            } else if (LogCache.this.logWriter.clearSpace()) {
                                if (LogCache.this.logWriter.rotate()) {
                                    LogCache.this.logWriter.println(msg);
                                }
                            } else {
                                Log.e("Logger", "can't log into sdcard.");
                            }
                        }
                    } catch (Throwable var4) {
                        if (var4 instanceof InterruptedException) {
                            throw (InterruptedException) var4;
                        }

                        if (var4 instanceof RuntimeException) {
                            throw (RuntimeException) var4;
                        }
                    }
                }

            }

            public void run() {
                try {
                    this.dealMsg();
                } catch (InterruptedException var2) {
                    Log.e("Logger", Thread.currentThread().toString(), var2);
                    LogCache.this.started = false;
                } catch (RuntimeException var3) {
                    Log.e("Logger", Thread.currentThread().toString(), var3);
                    LogCache.this.logWorkerThread = new Thread(LogCache.this.new LogTask(), "Log Worker Thread - " + LogCache.this.counter);
                    LogCache.this.started = false;
                }

            }
        }
    }

    private static class LogWriter {
        public static final String TAG = "Logger";
        private final Comparator<File> c = new Comparator<File>() {
            public int compare(File f1, File f2) {
                return String.CASE_INSENSITIVE_ORDER.compare(f1.getName(), f2.getName());
            }
        };
        private File current;
        private int fileAmount = 2;
        private long maxSize = 1048576L;
        private ArrayList<File> historyLogs = new ArrayList<>();
        private SimpleDateFormat timestampOfName = new SimpleDateFormat("yyyyMMddHHmmssSSS");
        private PrintWriter writer = null;
        private static LogWriter INSTANCE;

        private LogWriter() {
        }

        public static LogWriter getInstance(File current, int fileAmount, long maxSize) {
            if (INSTANCE == null) {
                INSTANCE = new LogWriter(current, fileAmount, maxSize);
            }

            return INSTANCE;
        }

        private LogWriter(File current, int fileAmount, long maxSize) {
            this.current = current;
            this.fileAmount = fileAmount <= 0 ? this.fileAmount : fileAmount;
            this.maxSize = maxSize <= 0L ? this.maxSize : maxSize;
            this.initialize();
        }

        public synchronized boolean initialize() {
            Log.v("Logger", "initializing... ");

            try {
                if (this.current == null) {
                    return false;
                } else {
                    if (!this.current.getParentFile().exists()) {
                        if (!this.current.getParentFile().mkdirs()) {
                            return false;
                        }
                    } else if (this.historyLogs == null) {
                        File[] fs = this.current.getParentFile().listFiles(new FilenameFilter() {
                            public boolean accept(File dir, String filename) {
                                String curName = LogWriter.this.current.getName();
                                String patt = curName.replace(curName.substring(curName.lastIndexOf(".")), "_");
                                return filename.contains(patt);
                            }
                        });
                        if (fs != null && fs.length != 0) {
                            this.historyLogs = new ArrayList(Arrays.asList(fs));
                        } else {
                            this.historyLogs = new ArrayList();
                        }
                    }

                    this.writer = new PrintWriter(new FileOutputStream(this.current, this.current.exists() && this.isCurrentAvailable()), true);
                    return true;
                }
            } catch (FileNotFoundException var2) {
                Log.e("LogWriter", "", var2);
                return false;
            }
        }

        private File getTheEarliest() {
            Collections.sort(this.historyLogs, this.c);
            return (File) this.historyLogs.get(0);
        }

        public boolean rotate() {
            File des = new File(this.newName());
            boolean result;
            if (this.historyLogs.size() >= this.fileAmount - 1) {
                result = forceDeleteFile(this.getTheEarliest());
                if (!result) {
                    Logger.i("Logger", "delete " + ((File) this.historyLogs.get(0)).getName() + "abortively.");
                    return false;
                }

                Logger.i("Logger", "old historyLogs: " + this.historyLogs);
                Logger.i("Logger", "delete " + ((File) this.historyLogs.get(0)).getName() + "successfully.");
                this.historyLogs.remove(0);
            }

            try {
                this.close();
                result = this.current.renameTo(des);
                if (!result || !this.initialize()) {
                    Log.v("Logger", "rename or initialize error!");
                    return false;
                }
            } catch (Exception var3) {
                Log.e("Logger", "", var3);
                return false;
            }

            this.historyLogs.add(des);
            Logger.i("Logger", "new historyLogs: " + this.historyLogs);
            return true;
        }

        public boolean isCurrentExist() {
            return this.current.exists();
        }

        public boolean isCurrentAvailable() {
            return this.current.length() < this.maxSize;
        }

        public String newName() {
            String name = this.current.getAbsolutePath();
            int dox = name.lastIndexOf(46);
            String prefix = name.substring(0, dox) + "_";
            String suffix = name.substring(dox);
            return prefix + this.timestampOfName.format(System.currentTimeMillis()) + suffix;
        }

        private boolean deleteTheEarliest() {
            return this.historyLogs.size() == 0 ? false : this.getTheEarliest().delete();
        }

        public void println(String msg) {
            if (this.writer == null) {
                this.initialize();
            } else {
                this.writer.println(msg);
            }

        }

        public boolean clearSpace() {
            return this.deleteTheEarliest();
        }

        public synchronized void close() {
            if (this.writer != null) {
                this.writer.close();
            }

        }

        private static boolean forceDeleteFile(File file) {
            boolean result = false;
            int var2 = 0;

            while (!result && var2++ < 10) {
                result = file.delete();
                if (!result) {
                    try {
                        synchronized (file) {
                            file.wait(200L);
                        }
                    } catch (InterruptedException var5) {
                        Logger.e("Logger", "forceDeleteFile:", var5);
                    }
                }
            }

            return result;
        }
    }

    private static class MemoryStatus {
        private static final int ERROR = -1;

        private MemoryStatus() {
        }

        public static long getAvailableExternalMemorySize() {
            if (Environment.getExternalStorageState().equals("mounted")) {
                File path = Environment.getExternalStorageDirectory();
                StatFs stat = new StatFs(path.getPath());
                long blockSize = (long) stat.getBlockSize();
                long availableBlocks = (long) stat.getAvailableBlocks();
                return availableBlocks * blockSize;
            } else {
                return -1L;
            }
        }

        public static boolean isExternalMemoryAvailable(long size) {
            long availableMemory = getAvailableExternalMemorySize();
            return size <= availableMemory;
        }
    }
}